﻿#ifndef SIGNALSLOT_H
#define SIGNALSLOT_H

#include <functional>
#include <memory>
#include <vector>
#include <metatype.h>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <set>
#include <algorithm>

#include "metatypedef_p.h"
#include <ReflectX_global.h>
#include "UniverseFunction.h"

template <typename ...Args>
class Signal;

class WorkService;

class ConnectionRegistry;
class ConnectionManager;

namespace ThreadSafe {
	class ThreadSafeBase;
}

class ConnectionManager
{
public:
    typedef  unsigned int SignalId;
    typedef unsigned int SlotId;
    typedef std::tuple<SignalId,SlotId> ConnectionId;
public:
    static ConnectionManager* Instance();
    class SlotObj;
    typedef std::shared_ptr<ConnectionManager::SlotObj> SlotObjPtr;
    class SignalData;
    typedef std::shared_ptr<SignalData> SignalDataPtr;

    ConnectionManager();

    SignalId RegisterSignal(const std::vector<MetaTypeId>& ArgumentTypes);
    void UnRegisterSignal(SignalId);
    std::vector<MetaTypeId> GetArgumentType(SignalId);
    ConnectionId Bind(SignalId, const FunctionAny &call, ThreadSafe::ThreadSafeBase* context, int type);
    void UnBind(ConnectionId);
    void Invoke(SignalId,const void** param);
private:
    SignalData* GetSignalData(SignalId);


private:
    struct Private;
    std::unique_ptr<Private> _P;
    friend class ConnectionRegistry;
    template <typename ...Args>
    friend class Signal;
    friend class GenericSignal;
};

template<typename ...Args>
std::vector<MetaTypeId> GetTypeList()
{
	static std::vector<MetaTypeId> ret = { MetaType::IdFromType<Args>()... };
	return ret;
}

class REFLECTX_EXPORT GenericSignal {
private:
	GenericSignal(const GenericSignal&);
	GenericSignal& operator = (const GenericSignal&);
public:
	enum ConnectType {
		Auto = 0,
		Direct,
		Queue,
	};

	GenericSignal(std::vector<MetaTypeId> senderType);
	~GenericSignal();

	std::vector<MetaTypeId> GetArgumentType();

	template<typename ...Args>
	ConnectionManager::ConnectionId DynamicBind(const std::function<void(Args...)>& fun, ThreadSafe::ThreadSafeBase* context = NULL, ConnectType type = ConnectType::Auto) {
		if (GetArgumentType() != GetTypeList<typename NormalizeType<Args>::Type...>()) {
			return ConnectionManager::ConnectionId(0, 0);
		}

		auto warped = WarpFunction(fun);

		return DoBind(warped, context, type);
	}

	ConnectionManager::ConnectionId DynamicBind(const std::function<void(const VariantList& param)>& fun, ThreadSafe::ThreadSafeBase* context = NULL, ConnectType type = ConnectType::Auto);

	ConnectionManager::ConnectionId UnSafeBind(const std::function<void(const void**param)>& fun, ThreadSafe::ThreadSafeBase* context = NULL, ConnectType type = ConnectType::Auto);

	template<typename ...Args>
	void DynamicInvoke(const Args& ... args) {
		if (GetArgumentType() != GetTypeList<typename NormalizeType<Args>::Type...>()) {
			return;
		}

		constexpr int psize_ = sizeof...(Args);
		constexpr int psize = std::max<int>(1, psize_);

		const void * param[psize] = { ((const void*)&args)... };
		DoInvoke(param);
	}

	void DynamicInvoke(const VariantList& param);

	void UnsafeInvoke(const void** param);

	template<typename ...Args>
	void operator()(Args... args) {
		DynamicInvoke(args...);
	}

	template<typename ...Args>
	Signal<Args...>* DownCast() {
		if (GetTypeList<Args...>() == GetArgumentType()) {
			return (Signal<Args...>*)this;
		}

		return NULL;
	}

	static void UnBind(ConnectionManager::ConnectionId id);

protected:
	ConnectionManager::ConnectionId DoBind(const FunctionAny & fun, ThreadSafe::ThreadSafeBase* context, ConnectType type);
	void DoInvoke(const void **param);
private:
	ConnectionManager::SignalId SignalId;
};

class REFLECTX_EXPORT ConnectionRegistry
{
    public:
    ConnectionRegistry();

    ~ConnectionRegistry();

    void Append(ConnectionManager::ConnectionId connection);
	void operator += (ConnectionManager::ConnectionId connection);
private:
    std::set<ConnectionManager::ConnectionId> data;

    template <typename ...Args>
    friend class Signal;

    friend class GenericSignal;
    friend class ConnectionManager;
};

template <typename ...Args>
class Signal:public GenericSignal
{

private:
    Signal(const Signal&);
    Signal& operator = (const Signal&);
public:
    typedef std::function<void(Args...)> SlotType;

    Signal():GenericSignal({MetaType::IdFromType<typename NormalizeType<Args>::Type>()...}){
    }
    ~Signal(){
    }

    ConnectionManager::ConnectionId Bind(SlotType slot,ThreadSafe::ThreadSafeBase* context = NULL,GenericSignal::ConnectType type = GenericSignal::Auto){
        return DynamicBind(slot,context,type);
    }

    void Invoke(const Args& ... args){
        DynamicInvoke(args...);
    }

    void operator()(const Args& ... args){
        Invoke(args...);
    }
private:
};

int hash(const ConnectionManager::ConnectionId& id);

#endif // SIGNALSLOT_H
